/**
 * @file 场景控制接口
 *
 * 场景控制接口
 *
 * @author wq1977@gmail.com
 */

#ifndef __SCENARIOS_CONTROL_H__
#define __SCENARIOS_CONTROL_H__

#include "ScenariosForm.h"
#include "OsipProcess.h"

typedef unsigned char U8;

void DumpMsgKeyField(int Uid, osip_message_t *pomt);

enum{
    STTEVT_BAD_EVENT=0,
    STTEVT_OSIP_EVENT,
    STTEVT_OSIP_TIMEOUT,
    STTEVT_TIMEROUT_EVENT,
    STTEVT_CASE_START,
    STTEVT_TERM_REGSTATE_CHANGE,
    STTEVT_SCEARIO_FINISH,
    STTEVT_CASE_FINISH,
};

class SttEvt{
public:
    SttEvt(int et){EventType = et; Param3=NULL; Param4=NULL;};
    SttEvt(int type, osip_transaction_t *pott,osip_message_t *pomt,int t=STTEVT_OSIP_EVENT){
        EventType=t;
        Param1=type;
        Param3=pott;
        Param4=pomt;
        };
    SttEvt(int Uid,wxString name,int t=STTEVT_TIMEROUT_EVENT){
        EventType=t;
        StrParam1 = name;
    	Param1=Uid;
	Param3=NULL; Param4=NULL;
        };
    SttEvt(int Uid,int t=STTEVT_CASE_START){
        EventType=t;
    	Param1=Uid;
	Param3=NULL; Param4=NULL;
        };
    ~SttEvt(){
      if (GetEvtType() == STTEVT_OSIP_EVENT){
	osip_message_free(GetOsipMsgPtr());
      }
    };
    int GetEvtType(){return EventType;};
    void *GetPointer(){return Param3;};
    void SetPointer(void *p){Param3=p;};
    int GetUid(){return Param1;};
    int GetTimerIdent(){ return Param2;};
    void SetTimerIdent(int v){ Param2=v;};
    wxString& GetTimerName(){return StrParam1;};
    int GetOsipTransType(){return Param1;};
    osip_transaction_t *GetOsipTransPtr(){return (osip_transaction_t *)Param3;};
    osip_message_t *GetOsipMsgPtr(){return (osip_message_t *)Param4;};
    void Dump(int Uid);
private:
    int EventType;  ///< SipEvent TimerEvent
    int Param1;     ///< use it as your wish
    int Param2;      ///< use it as your wish
    wxString StrParam1;
    void *Param3;   ///< When SipEvent,Convert to osip_event*
    void *Param4;   ///< When SipEvent,Convert to osip_event*
};

enum{
    CALL_CB_IDLE,
    CALL_CB_WAITEND,
    CALL_CB_WAITCASEEND,
    CALL_CB_ACTIVE,
    CALL_CB_WAITSTART,
};


WX_DECLARE_STRING_HASH_MAP(osip_transaction_t *, StrTransPtrHash);

class CallInfo{
    public:
    CallInfo(int i){State=CALL_CB_IDLE;UserIndexStart=-1; pCurSce=NULL; UsedTermNum=0; MyIndex=i; };
	int MyIndex;
	int UsedTermNum;
        int UserIndexStart;
	long SendRuleLineNo;
	Scenario *pCurSce;
        StrStrHash Varibles;
        StrTransPtrHash Transactions;
	wxArrayString Timers,exCallIds;
	void SetCallState(int state);
	int  GetCallState();
	void LogEvent(SttEvt *);
	void LogString(const wxString&);
	const wxString& GetLog(){return Log;};
	long GetId(){ return id;};
	void ClearLog(){ Log.Clear(); };
	wxDateTime GetStartTime(){return startat;};
 private:
        int State;
	long id;
	wxMutex s_mutexProtectingState;
	wxString Log;
	wxDateTime startat;
};


class SttScenarioControl;
#define AU_MAX_K_LEN    16
#define AU_MAX_SQN_LEN  6

WX_DEFINE_ARRAY(osip_dialog_t*, DialogPtrArray);

class TermControl{
public:
    osip_message_t *ToSend; ///< Before Send Action Call Send Rules, the message to send is put here.
    CallInfo *pCallInfo;
    StrStrHash Varibles;
    SttScenarioControl*controller;

    U8 K[AU_MAX_K_LEN*2+1];
    U8 SQNms[32][AU_MAX_SQN_LEN];
    std::string lastnonce;
    std::string lastAlgorithm;
    int RemotePort;

    TermControl(SttScenarioControl*c,int id, int ct=1){
        controller=c;
        Uid=id;
        Registed=false;
        CallTimes = ct;
        pCallInfo=NULL;
	FinalMark=false;
        Varibles.clear();
	memset(SQNms,0,sizeof(SQNms));
	memset(K,'0',sizeof(K));
	sprintf((char*)K,"ims%06d",Uid);
	K[9]='0';
	K[AU_MAX_K_LEN*2]=0;
	RemotePort=0;
    };

    ~TermControl();
    int  Uid;
    bool FinalMark;
    bool IsInThisCall(){
            if (!pCallInfo) return false;
            int idx = Uid - pCallInfo->UserIndexStart;
            return (idx>=0) && (idx<pCallInfo->UsedTermNum);
        };
    void AppendTracer(TraceForm *win){Traces.Append(win);};
    void Trace(int Level, const wxString& Value);
    void OnScenarioEnd(bool IsSucc=true, wxString Res=wxT(""));
    bool DoDefaultProcess(SttEvt *evt);
    void SetRegisted(bool r);
    wxString GetVaribleValue(const wxString &key);
    void SetVaribleValue(const wxString &key, wxString Value);
    bool IsRegisted(){
	return GetVaribleValue(wxT("RegistState"))==wxT("OK");
    };
    void DumpVarible(wxString prompt=wxT(""));
    int CaleResponse(std::string& Resp, std::string &Auts);
    bool CallIdMatch(const wxString &CallId, bool AllowAppend=false);
    void ClearDialogs(void);
    int GetLocalPort();
    void ClearRegistInfo();
    int DialogNum(){ return Dialogs.size();};
    osip_dialog_t *DialogItem(int index){ return Dialogs[index];};
    osip_dialog_t *GetDialog(osip_message_t *sip, int *Index=NULL);
    int AddDialog(osip_dialog_t* dialog);
    osip_message_t *GetCurrentMsg(SttEvt *evt=NULL);
    SttScenarioControl&GetController(){return *controller;};
private:
    wxList Traces;
    DialogPtrArray Dialogs;
    int CallTimes;
    bool Registed;
};

class SttScenarioControl;
/**
 * @defgroup ScenarioControlModule 场景执行模块
循环从自己的控制类中弹出一个事件进行处理,处理的过程是:
首先根据事件的类型获取终端实例,然后匹配并执行定义在默认场景中的规则,然后后匹配执行当前场景规则
最后执行hardcode的默认动作.

该线程只有在场景开始时产生,并且在场景结束时消亡

\sa ScenarioControlThread

*/


/*
#ifndef KEEP_s_mutexProtectingTheQueue
#define KEEP_s_mutexProtectingTheQueue
static wxMutex s_mutexProtectingTheQueue;
#endif
*/

WX_DEFINE_ARRAY(SttEvt *, EvtPtrArray);

//!  事件队列管理
/*!
    每一个场景执行拥有一个事件队列,管理新事件的压入和弹出
*/
class EventQueue{
public:
    void push_back(SttEvt *e){
        wxMutexLocker lock(s_mutexProtectingTheQueue);
        q.push_back(e);
        };
    SttEvt *pop(){
        SttEvt *ret=NULL;
        wxMutexLocker lock(s_mutexProtectingTheQueue);
        if (q.size()>0){
            ret=*(q.begin());
            q.erase(q.begin());
        }
        return ret;
        };
    void clear(){
        wxMutexLocker lock(s_mutexProtectingTheQueue);
		EvtPtrArray::iterator iter;
		for (iter=q.begin();iter!=q.end();iter++){
			SttEvt*p = *iter;
			delete(p);
		}
	    q.clear();
        };
    size_t depth(){
        return q.size();
        };
private:
    wxMutex s_mutexProtectingTheQueue;
    EvtPtrArray q;
};


//!  场景控制线程
/*!
\sa ScenarioControlModule
*/

class ScenarioControlThread: public wxThread
{
public:
    EventQueue m_EvtQueue;
    long MaxDepth;
 ScenarioControlThread(SttScenarioControl *sc):wxThread(wxTHREAD_JOINABLE){
        controller=sc;
	MaxDepth=0;
        Create();
        Run();
    };
    TermControl *GetTermInstFromEvt(SttEvt *evt);
    void ProcessSttEvt(SttEvt *,TermControl *);

 protected:
    void ProcessSttEvt(SttEvt *evt);
    virtual void *Entry();
    SttScenarioControl*controller;
};

WX_DEFINE_ARRAY(ScenarioControlThread*, CtrlThreadArray);
/**
 * @defgroup ScenarioControlPoolModule 场景执行线程池模块

循环尝试启动新的场景实例,并且根据控制类的事件队列深度和当前的场景执行线程个数比来决定是否需要增加或者减少执行线程的个数.

该线程只有在场景开始时产生,并且在场景结束时消亡

\sa ScenarioControlThread
\sa ScenarioControlModule

*/

class ScenarioControlThreadPool: public wxThread
{
public:
 ScenarioControlThreadPool(SttScenarioControl *sc,int tnum):wxThread(wxTHREAD_JOINABLE){
        controller=sc;
	ThreadNum=tnum;
        Create();
        Run();
      };
    ~ScenarioControlThreadPool();
    size_t depth(){ return pool.size(); };
    ScenarioControlThread*ThreadItem(size_t i){ return pool[i%pool.size()];};

protected:
    virtual void *Entry();

private:
    SttScenarioControl *controller;
    CtrlThreadArray pool;
    int ThreadNum;
};

class ScenariosForm;

class ReportPieceStru:public wxObject
{
    DECLARE_CLASS(ReportPieceStru);
 public:
    Scenario* s;
    wxDateTime timestamp;
    wxString Result;
};

WX_DECLARE_OBJARRAY(ReportPieceStru, RepPieceArray);

typedef struct{
  bool FirstFail;
  int TotalRun;
  int TotalSucc;
  int TotalFail;
  wxString FirstFailReason;
  wxDateTime FirstFailTime;
}SubReportPiece;

WX_DECLARE_VOIDPTR_HASH_MAP(SubReportPiece, ScePieceHash);

typedef struct{
    long Num;
    wxDateTime time;
}NumDateStru;
WX_DECLARE_STRING_HASH_MAP(NumDateStru, Str_NumDate_Hash);

#define MAX_SCENARIOS_LOG 5
class SttReport{
 public:
  wxFileName Base;
  SttReport();
  void TouchReport(Scenario* s, wxString Result);
  void TouchReport(int msgtype, osip_message_t* sip);
  void TouchReport(wxString SceLog);
  void TouchReport(wxString MaxKey, long MaxValue);
  void TouchReportStatistic(wxString Key);
  void SaveToReport();
 private:
  //RepPieceArray pieces;
  wxArrayString SceLogs;
  StrLongHash RcvMsgStatistic,SntMsgStatistic,UserStatistic;
  Str_NumDate_Hash MaxValus;
  wxDateTime start;

  SubReportPiece stGlobal;
  Scenario* GlobalFirstFail;
  ScePieceHash ret;

};

WX_DEFINE_ARRAY(TermControl*, TCPtrArray);
WX_DEFINE_ARRAY(CallInfo*, CIPtrArray);
WX_DEFINE_ARRAY(osip_transaction_t *, OTPtrArray);

class SttScenarioControl{
    //Allow ScenarioControlThread to access my private members.
    friend class ScenariosResultGrid;
    friend class ScenarioControlThreadPool;
    friend class ScenarioControlThread;
public:
    bool SafeStoped;
    bool Terminated;
    int FinalStyle;

    SttScenarioControl(ScenariosForm *eh, Scenario* s, int TermNum, int totaltimes,
		       int calldur, int callhold, int callheavy, int finalStyle);
    SttScenarioControl(ScenariosForm *eh, int CaseStyle, int TermNum, int totaltimes,
        int calldur, int callhold, int callheavy, int finalStyle);
    ~SttScenarioControl();

    TermControl *GetTermCtrl(int Index);
    ScenariosForm *GetEvtHandler(){return Handle;};
    void AddRunningScenarioNum(int num=1);
    bool DecRunningScenarioNum(int num=1, bool IsSucc=true, wxString Res=wxT(""));
    bool AppendEvent(SttEvt*, int);
    void OnTermRegistStateChange(int TermId, bool newv);
    void TestStartCallGroup(void);
    void DoReport();
    bool SetSafeStop(bool Stop=true);
    wxFileName GetReportFileName(){ return report->Base; };
    void TouchReport(Scenario* s, wxString Result);
    void TouchReport(int msgtype,osip_message_t* sip);
    void TouchReport(wxString MaxKey, long MaxValue);
    void TouchReport(wxString SceLog);
    void TouchReportStatistic(wxString Key);
    void Quit();
    int GetTotalLeft();
    void AddTransaction(osip_transaction_t*s){AllTrans.push_back(s);};
    bool IsAllTransRemoved();
    void TestAndRemoveDeadTrans(int Num=-1);
private:
    int LastIdleCallIdx;
    int TestStyle;
    wxDateTime LastTestTime;
    int CurRoundTestSceIndex;
    ScenariosForm *Handle;
    Scenario* CurScenario;
    SttReport* report;
    TCPtrArray AllTerms;
    CIPtrArray AllCallInfos;
    OTPtrArray AllTrans;
    //std::vector <StrTransPtrHash*> AllStoredTransGroup;
    //EventQueue m_EvtQueue;
    ScenarioControlThreadPool* threadpool;
    int OriTotal,TotalTimes,CallDuration,CallHold,CallHeavy;
};


#endif

